#include <common.h>
#include "syscall.h"
#include "fs.h"
#include "proc.h"

#define SRING_BUFF_SIZE 128
struct _SRing {
  uint64_t gpr1,gpr2,gpr3,gpr4;
} sring_buff[SRING_BUFF_SIZE] = {0};
static int sring_buff_index = 0;

void sring_save_fun(uint64_t gpr1, uint64_t gpr2, uint64_t gpr3, uint64_t gpr4 ) {
  sring_buff[sring_buff_index%SRING_BUFF_SIZE].gpr1 = gpr1;
  sring_buff[sring_buff_index%SRING_BUFF_SIZE].gpr2 = gpr2;
  sring_buff[sring_buff_index%SRING_BUFF_SIZE].gpr3 = gpr3;
  sring_buff[sring_buff_index%SRING_BUFF_SIZE].gpr4 = gpr4;
  sring_buff_index ++;
}
void print_sring_buff(void) {
  int syscall_cnt = sring_buff_index;
  Log("sring buff:%d",syscall_cnt);
  int idx = 0;
  if(syscall_cnt > SRING_BUFF_SIZE) {
    syscall_cnt = SRING_BUFF_SIZE;
    idx = sring_buff_index;
  }
  while (syscall_cnt--)
  {
    struct _SRing sring = sring_buff[idx%SRING_BUFF_SIZE];
    switch (sring.gpr1)
    {
    case SYS_exit:  Log("exit : %d",sring.gpr2); break;
    case SYS_yield: Log("yield  "); break;
    case SYS_open:  Log("open : %s",(char*)sring.gpr2); break;
    case SYS_read:  Log("read : %s  buf:%p  len:%d", fs_getname(sring.gpr2),(void*)sring.gpr3,sring.gpr4); break;
    case SYS_write: Log("write: %s  buf:%p  len:%d",fs_getname(sring.gpr2),(void*)sring.gpr3,sring.gpr4); break;
    case SYS_close: Log("close: %s",fs_getname(sring.gpr2)); break;
    case SYS_lseek: Log("lseek: %s  offset:%d  whence=%d:%s",fs_getname(sring.gpr2),sring.gpr3,sring.gpr4,sring.gpr4==SEEK_SET?"SEEK_SET":sring.gpr4==SEEK_CUR?"SEEK_CUR":sring.gpr4==SEEK_END?"SEEK_END":"SEEK_OTHER"); break;
    case SYS_brk:   Log("brk  : %d",sring.gpr2); break;
    default:
      Log("GPR1:%p GPR2:%p GPR3:%p GPR4:%p",sring.gpr1,sring.gpr2,sring.gpr3,sring.gpr4);
      break;
    } 
    idx++;
  }
  
}

int sys_brk(int increment) {

  return 0;
}

int sys_execve(const char *pathname, char *const argv[],char *const envp[]) {
  
  void naive_uload(PCB *pcb, const char *filename);
  naive_uload(NULL, pathname);
  return 0;
}

size_t sys_write(int fd, void*buf, size_t count) {
  char *p = buf;
  switch (fd)
  {
  case 1://stdout
  case 2://stderr
    for (int i = 0; i < count; i++) putch(*p++);
    return count;
    break;
  
  default: assert(0);
    break;
  }
  return -1;
}
uint64_t sys_getus() {
  AM_TIMER_UPTIME_T t = io_read(AM_TIMER_UPTIME);
  return t.us;
}
void do_syscall(Context *c) {
  uintptr_t a[4];
  a[0] = c->GPR1;
  a[1] = c->GPR2;
  a[2] = c->GPR3;
  a[3] = c->GPR4;
  sring_save_fun(a[0],a[1],a[2],a[3]);
  switch (a[0]) {
    case SYS_exit: sys_execve("/bin/menu",0,0); halt(a[1]); break;
    case SYS_yield: yield(); c->GPRx = 0; break;
    case SYS_open: c->GPRx = fs_open((const char*)a[1],a[2],a[3]);break;
    case SYS_read: c->GPRx = fs_read(a[1],(void*)a[2],a[3]);break;
    case SYS_write: c->GPRx = fs_write(a[1],(void*)a[2],a[3]);break;

    case SYS_close: c->GPRx = fs_close(a[1]);break;
    case SYS_lseek: c->GPRx = fs_lseek(a[1],a[2],a[3]);break;

    case SYS_brk: c->GPRx = sys_brk(a[1]); break;
    case SYS_execve: c->GPRx = sys_execve((const char *)a[1],( char *const*)a[2],(char *const*)a[3]); break;
    case SYS_gettimeofday: c->GPRx = sys_getus(); break;
    case -1: Log("yield"); break;
    default: panic("Unhandled syscall ID = %d", a[0]);
  }

}
